package org.aceor.mddal.group;

import org.aceor.mddal.common.constants.Constants;
import org.aceor.mddal.common.sqlparser.util.SQLCommonUtil;
import org.aceor.mddal.group.selector.DBSelector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.*;

/**
 * Created by lxue on 16/4/8.
 */
public class GroupStatement implements Statement {

    private static final Logger log = LoggerFactory.getLogger(GroupStatement.class);

    protected GroupDataSource groupDataSource;
    protected GroupConnection groupConnection;

    protected Statement baseStatement;

    //重试次数
    protected int retryingTimes = Constants.DEFAULT_RETRY_TIMES;

    //透传
    protected int resultSetType;
    protected int resultSetConcurrency;
    protected int resultSetHoldability;
    protected int autoGeneratedKeys;
    protected int[] columnIndexes;
    protected String[] columnNames;

    //tryer
    protected DBSelector.DataSourceTryer<ResultSet> executeQueryTryer = new DBSelector.AbstractDataSourceTryer<ResultSet>() {
        @Override
        public ResultSet tryOnDataSource(DataSourceWrapper dsw, Object... args) throws SQLException {
            String sql = (String) args[0];
            //第一次创建读连接.
            Connection conn = groupConnection.createNewConnection(dsw, true);
            return executeQueryOnConnection(conn, sql);
        }
    };

    protected DBSelector.DataSourceTryer<Integer> executeUpdateTryer = new DBSelector.AbstractDataSourceTryer<Integer>() {
        @Override
        public Integer tryOnDataSource(DataSourceWrapper dsw, Object... args) throws SQLException {
            Connection conn = groupConnection.createNewConnection(dsw, false);
            //String sql, int autoGeneratedKeys, int[] columnIndexes, String[] columnNames ==> 五个参数
            return executeUpdateOnConnection(conn, (String) args[0], (Integer) args[1], (int[]) args[2],
                    (String[]) args[3]);
        }
    };

    public GroupStatement(GroupDataSource groupDataSource, GroupConnection groupConnection) {
        this.groupDataSource = groupDataSource;
        this.groupConnection = groupConnection;
    }

    @Override
    public ResultSet executeQuery(String sql) throws SQLException {

        boolean isRead = SQLCommonUtil.isQuerySql(sql) && groupConnection.getAutoCommit();
        Connection connection = groupConnection.getBaseConnection(sql, isRead);
        if (connection != null) {
            //连接复用
            return executeQueryOnConnection(connection, sql);
        } else {
            //通过selector获取数据源,成功获取数据源会执行DataSourceTryer.tryOnDataSource
            //该方法实现自定义,用获得的数据源第一次创建connection,并执行sql
            return groupDataSource.getRuntimeSelectorHolder().getDBSelector(isRead).tryExecute(executeQueryTryer, retryingTimes, sql);
        }
    }

    public int executeUpdate(String sql) throws SQLException {
        return executeUpdateInternal(sql, -1, null, null);
    }

    public boolean execute(String sql) throws SQLException {
        return executeInternal(sql, -1, null, null);
    }

    private int executeUpdateInternal(String sql, int autoGeneratedKeys, int[] columnIndexes, String[] columnNames)
            throws SQLException {
        Connection conn = groupConnection.getBaseConnection(sql, false);
         if (null != conn) {
             return executeUpdateOnConnection(conn, sql, autoGeneratedKeys, columnIndexes, columnNames);
         } else {
             return groupDataSource.getRuntimeSelectorHolder().getDBSelector(false).tryExecute(executeUpdateTryer, retryingTimes, sql);
         }
    }

    protected ResultSet executeQueryOnConnection(Connection connection, String sql) throws SQLException {
        return createStatementInternal(connection).executeQuery(sql);
    }

    private int executeUpdateOnConnection(Connection conn, String sql, int autoGeneratedKeys, int[] columnIndexes, String[] columnNames) throws SQLException {
        Statement stmt = createStatementInternal(conn);
        if (autoGeneratedKeys == -1 && columnIndexes == null && columnNames == null) {
            return stmt.executeUpdate(sql);
        } else if (autoGeneratedKeys != -1) {
            return stmt.executeUpdate(sql, autoGeneratedKeys);
        } else if (columnIndexes != null) {
            return stmt.executeUpdate(sql, columnIndexes);
        } else if (columnNames != null) {
            return stmt.executeUpdate(sql, columnNames);
        } else {
            return stmt.executeUpdate(sql);
        }
    }

    private Statement createStatementInternal(Connection conn) throws SQLException {
        Statement statement = conn.createStatement();
        setBaseStatement(statement);
        return statement;
    }

    @Override
    public void close() throws SQLException {
        close(true);
    }

    @Override
    public int getMaxFieldSize() throws SQLException {
        return 0;
    }

    @Override
    public void setMaxFieldSize(int max) throws SQLException {

    }

    @Override
    public int getMaxRows() throws SQLException {
        return 0;
    }

    @Override
    public void setMaxRows(int max) throws SQLException {

    }

    @Override
    public void setEscapeProcessing(boolean enable) throws SQLException {

    }

    @Override
    public int getQueryTimeout() throws SQLException {
        return 0;
    }

    @Override
    public void setQueryTimeout(int seconds) throws SQLException {

    }

    @Override
    public void cancel() throws SQLException {

    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        return null;
    }

    @Override
    public void clearWarnings() throws SQLException {

    }

    @Override
    public void setCursorName(String name) throws SQLException {

    }

    @Override
    public ResultSet getResultSet() throws SQLException {
        return null;
    }

    @Override
    public int getUpdateCount() throws SQLException {
        return 0;
    }

    @Override
    public boolean getMoreResults() throws SQLException {
        return false;
    }

    @Override
    public void setFetchDirection(int direction) throws SQLException {

    }

    @Override
    public int getFetchDirection() throws SQLException {
        return 0;
    }

    @Override
    public void setFetchSize(int rows) throws SQLException {

    }

    @Override
    public int getFetchSize() throws SQLException {
        return 0;
    }

    @Override
    public int getResultSetConcurrency() throws SQLException {
        return resultSetConcurrency;
    }

    @Override
    public int getResultSetType() throws SQLException {
        return resultSetType;
    }

    @Override
    public void addBatch(String sql) throws SQLException {

    }

    @Override
    public void clearBatch() throws SQLException {

    }

    @Override
    public int[] executeBatch() throws SQLException {
        return new int[0];
    }

    @Override
    public Connection getConnection() throws SQLException {
        return null;
    }

    @Override
    public boolean getMoreResults(int current) throws SQLException {
        return false;
    }

    @Override
    public ResultSet getGeneratedKeys() throws SQLException {
        return null;
    }

    public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
        return executeUpdateInternal(sql, autoGeneratedKeys, null, null);
    }

    public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
        return executeUpdateInternal(sql, -1, columnIndexes, null);
    }

    public int executeUpdate(String sql, String[] columnNames) throws SQLException {
        return executeUpdateInternal(sql, -1, null, columnNames);
    }

    public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
        return executeInternal(sql, autoGeneratedKeys, null, null);
    }

    public boolean execute(String sql, int[] columnIndexes) throws SQLException {
        return executeInternal(sql, -1, columnIndexes, null);
    }

    public boolean execute(String sql, String[] columnNames) throws SQLException {
        return executeInternal(sql, -1, null, columnNames);
    }

    //jdbc规范: 返回true表示executeQuery，false表示executeUpdate
    private boolean executeInternal(String sql, int autoGeneratedKeys, int[] columnIndexes, String[] columnNames)
            throws SQLException {
        if (SQLCommonUtil.isQuerySql(sql)) {
            executeQuery(sql);
            return true;
        }
        else {
            if (autoGeneratedKeys == -1 && columnIndexes == null && columnNames == null) {
                executeUpdate(sql);
            } else if (autoGeneratedKeys != -1) {
                executeUpdate(sql, autoGeneratedKeys);
            } else if (columnIndexes != null) {
                executeUpdate(sql, columnIndexes);
            } else if (columnNames != null) {
                executeUpdate(sql, columnNames);
            } else {
                executeUpdate(sql);
            }

            return false;
        }
    }

    @Override
    public int getResultSetHoldability() throws SQLException {
        return resultSetHoldability;
    }

    @Override
    public boolean isClosed() throws SQLException {
        return baseStatement.isClosed();
    }

    @Override
    public void setPoolable(boolean poolable) throws SQLException {

    }

    @Override
    public boolean isPoolable() throws SQLException {
        return false;
    }

    @Override
    public void closeOnCompletion() throws SQLException {

    }

    @Override
    public boolean isCloseOnCompletion() throws SQLException {
        return false;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    public GroupDataSource getGroupDataSource() {
        return groupDataSource;
    }

    public void setGroupDataSource(GroupDataSource groupDataSource) {
        this.groupDataSource = groupDataSource;
    }

    public GroupConnection getGroupConnection() {
        return groupConnection;
    }

    public void setGroupConnection(GroupConnection groupConnection) {
        this.groupConnection = groupConnection;
    }

    public void setResultSetType(int resultSetType) {
        this.resultSetType = resultSetType;
    }

    public void setResultSetConcurrency(int resultSetConcurrency) {
        this.resultSetConcurrency = resultSetConcurrency;
    }

    public void setResultSetHoldability(int resultSetHoldability) {
        this.resultSetHoldability = resultSetHoldability;
    }

    public int getAutoGeneratedKeys() {
        return autoGeneratedKeys;
    }

    public void setAutoGeneratedKeys(int autoGeneratedKeys) {
        this.autoGeneratedKeys = autoGeneratedKeys;
    }

    public int[] getColumnIndexes() {
        return columnIndexes;
    }

    public void setColumnIndexes(int[] columnIndexes) {
        this.columnIndexes = columnIndexes;
    }

    public String[] getColumnNames() {
        return columnNames;
    }

    public void setColumnNames(String[] columnNames) {
        this.columnNames = columnNames;
    }

    public void close(boolean isRemove) throws SQLException {
        try {
            if (null != baseStatement) {
                baseStatement.close();
            }
        } finally {
            if (isRemove) {
                groupConnection.removeOpenedStatements(baseStatement);
            }
        }
    }

    public Statement getBaseStatement() {
        return baseStatement;
    }

    public void setBaseStatement(Statement baseStatement) throws SQLException {
        if (this.baseStatement != null) {
            this.baseStatement.close();
        }
        this.baseStatement = baseStatement;
    }
}
